この項では、データ・ストア内の選択した表への変更を検出およびレポートするXLAアプリケーションの一般的な作成手順について説明します。「列データの確認」のような例外もありますが、この項で説明する手順は、ほぼすべてのXLAアプリケーションに適用できます。
この項で説明する手順は次のとおりです。
この項のコード例は、install_dir/demo/xlaSimple.cデモ・アプリケーションに基づいています。このデモのコンパイル方法の詳細は、READMEファイルおよび「XLAアプリケーションのコンパイルおよびリンク」を参照してください。このデモと互換性のあるTimesTenデータ・ストアの設定方法などの操作の詳細およびいくつかの入力例については、「xlaSimpleデモ」を参照してください。
XLAアプリケーションでは、標準Cライブラリのみでなく、次のTimesTen固有のファイルをインクルードする必要があります。
インクルード・ファイル
|
説明
|
---|---|
timesten.h | TimesTen ODBCインクルード・ファイル |
tt_errCode.h | TimesTenネイティブ・エラー・コード |
tt_xla.h | TimesTen XLAインクルード・ファイル |
この項では、「データ・ストアに対する接続および切断」で説明されている一般的なTimesTenの操作を再確認します。
すべてのODBCアプリケーションと同様に、XLAアプリケーションは、最初にODBCを初期化し、環境ハンドル(henv)を取得する必要があります。その後、接続ハンドル(hdbc)を取得して、特定のTimesTenデータ・ストアと通信します。
環境ハンドルおよび接続ハンドルを初期化します。
SQLHENV henv = SQL_NULL_HENV; SQLHDBC hdbc = SQL_NULL_HDBC;henvのアドレスをSQLAllocEnv()関数に渡して環境ハンドルを割り当てます。
rc = SQLAllocEnv(&henv);hdbcのアドレスをSQLAllocConnect()関数に渡してTimesTenデータ・ストア用の接続ハンドルを割り当てます。
rc = SQLAllocConnect(henv, &hdbc);SQLDriverConnect()ファンクションをコールして、接続文字列(connStr)で指定されたデータ・ストアに接続します。この接続文字列は、この例ではコマンドラインから渡されます。
char connStr[256]; char conn_str_out[1024]; rc = SQLDriverConnect(hdbc, NULL, (SQLCHAR*)connStr, SQL_NTS, (SQLCHAR*)conn_str_out, sizeof(conn_str_out), NULL, SQL_DRIVER_NOPROMPT);
SQLSetConnectOption()関数をコールして自動コミットをOFFにします。これは、デフォルトではONに設定されています。
「データ・ストア接続ハンドルの取得」の説明に従ってODBCを初期化し、環境ハンドルおよび接続ハンドルを取得した後、XLAを初期化し、XLAハンドル(xla_handle)を取得して、トランザクション・ログにアクセスできます。1つのODBC接続に対して1つのみのXLAハンドルを作成します。アプリケーションで複数のリーダー・スレッドを使用する場合は、スレッドごとに個別のXLAハンドルおよびODBC接続を作成します。
TimesTenは、永続モードまたは非永続モードで初期化できます(「永続および非永続のXLAモード」を参照)。この項では、ほぼすべてのXLAアプリケーションで使用するモードである永続モードでXLAを初期化する方法について説明します。特別な理由から非永続モードでXLAを使用する必要がある場合は、「非永続モードでのXLAの初期化」を参照してください。
XLAを初期化する前に、ブックマーク(「XLAブックマークについて」を参照)およびXLAハンドルをttXlaHandle_h型として、次のように初期化する必要があります。
char bookmarkName [32] ; strcpy(bookmarkName, "xlaSimple"); ttXlaHandle_h xla_handle = NULL;bookmarkNameおよびxla_handleのアドレスをttXlaPersistOpen関数に渡してXLAハンドルを取得します。
rc = ttXlaPersistOpen(hdbc, bookmarkName, XLACREAT, &xla_handle);
XLACREATオプションは、新しいブックマークの作成を指定します。同じ名前のブックマークがすでに存在する場合は、次のように、XLAREUSEオプションを指定してttXlaPersistOpenをコールし、既存のブックマークを再利用します。
#include <tt_errCode.h> /* TimesTen error file */ if ( native_error == tt_ErrKeyExists ) { rc = ttXlaPersistOpen(hdbc, bookmarkName, XLAREUSE, &xla_handle); }
「XLAの初期化およびXLAハンドルの取得」の説明に従ってXLAを初期化し、xla_handleを取得した後、更新イベントを監視する表またはマテリアライズド・ビューを指定できます。
まず、ttXlaTableByName関数をコールして、指定した表またはマテリアライズド・ビューのシステム識別子およびユーザー識別子の両方を取得します。次に、ttXlaTableStatus関数をコールして、表またはマテリアライズド・ビューへの変更を監視するためにXLAを有効にします。
この例では、SCOTT.MYDATA表への変更を追跡します。
#define TABLE_OWNER "SCOTT" #define TABLE_NAME "MYDATA" SQLUBIGINT SYSTEM_TABLE_ID = 0; SQLUBIGINT userID; rc = ttXlaTableByName(xla_handle, TABLE_OWNER, TABLE_NAME, &SYSTEM_TABLE_ID, &userID);表の識別子を取得すると、ttXlaTableStatus関数を使用して、SCOTT.MYDATA表への変更を検出するためのXLA更新追跡を有効にできます。newstatusパラメータを0(ゼロ)以外の値に設定すると、指定した表に行われた変更がXLAによって追跡されます。
SQLINTEGER oldstatus; SQLINTEGER newstatus = 1; rc = ttXlaTableStatus(xla_handle, SYSTEM_TABLE_ID, 0, &oldstatus, &newstatus);oldstatusパラメータは、コール時の表のステータスを示す出力です。
ttXlaTableStatusでnewstatusをNULLにし、oldstatusのみを返すことによって、表の現行のXLAステータスを返すことができます。次に例を示します。
rc = ttXlaTableStatus(xla_handle, SYSTEM_TABLE_ID, 0, &oldstatus, NULL); if (oldstatus != 0) printf("XLA is currently tracking changes to table %s.%s\n", TABLE_OWNER, TABLE_NAME); else printf("XLA is not tracking changes to table %s.%s\n", TABLE_OWNER, TABLE_NAME);
更新を監視する表を指定した後、ttXlaNextUpdateまたはttXlaNextUpdateWait関数をコールして、トランザクション・ログから一連のレコードを返すことができます。コミット済トランザクションのレコードのみが、コミットされた順序で返されます。ttXlaAcknowledge関数を定期的にコールして、トランザクションの受信を確認する必要があります。これによって、不要になったパージ可能なレコードを判別できます。これらの関数は、トランザクション・ログ内のアプリケーションのブックマークの位置に影響を与えます(XLAブックマークについての項を参照)。
ttXlaNextUpdateによって返されたトランザクション内の各更新レコードは、ttXlaUpdateDesc_t構造体で記述される更新ヘッダーで始まります。この更新ヘッダーには、レコードがトランザクションの最初のレコードか(TT_UPDFIRST)、最後のコミット・レコード(TT_UPDCOMMIT)を示すフラグが格納されています。また、更新ヘッダーは、更新によって影響を受ける表も識別します。更新ヘッダーの後には、データ・ストア内の表に対して行われた変更について記述する0(ゼロ)から2行の行データが続きます。
図3.5に、4つの更新レコードで構成されているトランザクションをログから返すttXlaNextUpdateへのコールを示します。返されたトランザクションの受信は、ttXlaAcknowledgeをコールすることで確認され、ブックマークが再設定されます。
図3.5 トランザクション・ログ・レコード
xlaSimple.cデモでは、表の更新に対する監視は、ユーザーが停止するまで続行されます。
ttXlaNextUpdateWaitをコールする前に、トランザクション・ログから返されるレコードの最大数(MAX_RECORDS)および実際に返されるレコード数を保持する変数(records)を指定するのみでなく、返されるttXlaUpdateDesc_tレコードを保持するバッファへのポインタ(arry)を初期化します。また、ttXlaNextUpdateWaitをコールするため、トランザクション・ログ・バッファでレコードが見つからない場合に待機する秒数(FETCH_WAIT_SECS)も指定します。
次に、ttXlaNextUpdateWaitをコールし、これらの値を渡してarry内の一連のttXlaUpdateDesc_tレコードを取得します。例3.4に示すように、arry内の各レコードは、HandleChange()関数に渡すことによって処理されます。すべてのレコードの処理後、ttXlaAcknowledgeをコールして、ブックマークの位置を再設定します。
#define FETCH_WAIT_SECS 5 #define MAX_RECORDS 100 SQLINTEGER records; ttXlaUpdateDesc_t ** arry; int j; while (!StopRequested()) { /* Get a batch of update records */ rc = ttXlaNextUpdateWait(xla_handle, &arry, MAX_RECORDS, &records, FETCH_WAIT_SECS); if (rc != SQL_SUCCESS { /* See "Handling XLA errors" on page 67 */ } /* Process the records */ for(j=0; j < records; j++){ ttXlaUpdateDesc_t *p; p = arry[j]; HandleChange(p); /* Described in the next section */ } /* After each batch, Acknowledge updates to reset bookmark.*/ rc = ttXlaAcknowledge(xla_handle); if (rc != SQL_SUCCESS { /* See "Handling XLA errors" on page 67 */ } } /* end while !StopRequested() */ttXlaNextUpdate またはttXlaNextUpdateWaitによって返されるレコードの実際の数は、nreturned出力パラメータで示されるように、maxrecords パラメータの値より小さい場合があります。Figure 3.6に、例を示します。この例では、maxrecordsが10で、7つのレコードで構成されているトランザクションATおよび3つのレコードで構成されているトランザクションBTがログに含まれています。この場合、両方のトランザクションが同じバッチで返され、maxrecordsおよびnreturnedの両方の値が10になります。ただし、ログ内のその次の3つのトランザクションは、11個のレコードで構成されているCT、2つのレコードで構成されているDTおよび2つのレコードで構成されているETになります。CTのコミット・レコードの前にDTのコミット・レコードが書き込まれるため、次にttXlaNextUpdateをコールすると、DTトランザクションの2つのレコードが返され、nreturnedの値は2になります。その次にttXlaNextUpdateをコールすると、XLAによって、CTトランザクションの合計レコード数がmaxrecordsを超えていることが検出され、このトランザクションのレコードが2つのバッチで返されます。1つ目のバッチには、CTトランザクションの最初の10個のレコード(nreturned = 10)が含まれます。2つ目のバッチには、CTトランザクションの最後のレコードおよびETトランザクションの2つのレコード(ETトランザクションの後のトランザクションのコミット・レコードが次の7つのレコード内で検出されない場合)が含まれます。
図3.6 maxrecords=10の場合に取得されるレコード
XLAは、レコードをメモリー・バッファまたはログ・ファイルから読み取ります(「XLAでレコードをトランザクション・ログから読み取る方法」を参照)。待機時間を最小にするために、メモリー・バッファからのレコードは使用可能になるとすぐに返されますが、バッファにないレコードはバッファが空の場合にのみ返されます。この設計によって、XLAアプリケーションでは、行われた変更を最小の待機時間ですぐに表示できます。トレードオフとして、ttXlaNextUpdateまたはttXlaNextUpdateWaitのmaxrecordsパラメータでリクエストした数より少ない数の変更が返される場合があります。
更新レコードの配列の存在、および各レコードが示す処理のタイプが確認されたため、この項では、返される行データを確認します。
ttXlaNextUpdateまたはttXlaNextUpdateWait関数によって返される各レコードは、処理が実行された表について記述するttXlaUpdateDesc_tヘッダーで始まります。このヘッダーには、レコードがトランザクションの最初のレコードか最後の(コミット・)レコードか、レコードが示す処理のタイプ、返された行データの長さ(該当する行データがある場合)、行内の更新された列(該当する列がある場合)などの情報が記述されます。
図3.7 XLA更新レコードに返される行データのアドレス
ttXlaUpdateDesc_tヘッダーは固定長で、処理のタイプに応じて、データ・ストアから0(ゼロ)から2行の行(タプル)が続きます。ttXlaUpdateDesc_tのアドレスを取得し、それにsizeof ttXlaUpdateDesc_tを追加することによって、最初に返された行のアドレスを検出できます。
tup1 = (void*) ((char*) ttXlaUpdateDesc_t + sizeof(ttXlaUpdateDesc_t));ttXlaUpdateDesc_t -> typeフィールドは、更新を生成したSQL処理についての記述です。UPDATETTUP型のトランザクション・レコードは、UPDATE処理についての記述であるため、更新の前後の行データをレポートするために2行の行を返します。afterという値が含まれている2番目に返された行のアドレスは、レコード内の1つ目の行のアドレスをその長さに追加すると検出できます。
if (ttXlaUpdateDesc_t->type == UPDATETUP) { tup2 = (void*) ((char*) tup1 + ttXlaUpdateDesc_t->tuple1); }
例3.3では、ttXlaNextUpdateWait関数によって返された各レコードをHandleChange()関数に渡します。この関数は、レコードがINSERT、UPDATEまたはCREATE VIEWのいずれの処理と関連しているかを判別します。この例では、わかりやすくするために、その他の処理はすべて無視されています。
HandleChange()関数は、PrintColValues()関数(例3.16を参照)をコールする前に各タイプのSQL処理を異なる方法で処理します。
void HandleChange(ttXlaUpdateDesc_t* xlaP) { void * tup1; void * tup2; tup1 = (void*) ((char*) xlaP + sizeof(ttXlaUpdateDesc_t)); switch(xlaP->type) { case INSERTTUP: /* Handle INSERT operations */ printf("Inserted new row:\n"); PrintColValues(tup1); break; case UPDATETUP: /* Handle UPDATE operations */ tup2 = (void*) ((char*) tup1 + xlaP->tuple1); printf("Updated row:\n"); PrintColValues(tup1); printf("To:\n"); PrintColValues(tup2); break; case DELETETUP: /* Handle DELETE operations */ printf("Deleted row:\n"); PrintColValues(tup1); break; default: /* Ignore all other operations */ break; } }
更新レコードでは、0(ゼロ)から2行の行がttXlaUpdateDesc_t構造体の後に返される場合があります(「レコード・ヘッダーの確認および行アドレスの検出」を参照)。図3.8に示すように、各行のデータの最初の部分は固定長で、その後に可変長データが続きます。
図3.8 XLA更新レコードに返される行内の列オフセット
列データを確認する手順は次のとおりです。
返された行から列値を読み取るには、まず、その行の各列のオフセットを判別する必要があります。列オフセットおよびその他の列メタデータは、特定の表に対してttXlaGetColumnInfo関数をコールすると取得できます。この関数は表の列ごとに個別のttXlaColDesc_t構造体を返します。ttXlaGetColumnInfo関数は、初期化プロシージャの一部としてコールする必要があります。このコールについては、この項で詳細を説明しているため、「XLAの初期化およびXLAハンドルの取得」での説明では省略されています。
ttXlaGetColumnInfoをコールする場合は、colinfoパラメータを指定して、返されるttXlaColDesc_t構造体のリストを保持するバッファへのポインタを作成します。バッファのサイズは、maxcolsパラメータを使用して定義します。
次に示すxlaSimple.cデモのコード例では、返される列の最大数(MAX_XLA_COLUMNS)を推測します。これは、返されるttXlaColDesc_t構造体を保持するバッファのサイズ(xla_column_defs)を設定します。maxcolsパラメータを設定する場合のもう1つのより正確な方法として、ttXlaGetTableInfo関数をコールした後で、ttXlaTblDesc_t→columnsに返された値を使用する方法があります(install_dir/demo/xlaPersistent/subscriber.c.のinitialize関数を参照)。
#define MAX_XLA_COLUMNS 128 SQLINTEGER ncols; ttXlaColDesc_t xla_column_defs[MAX_XLA_COLUMNS]; rc = ttXlaGetColumnInfo(xla_handle, SYSTEM_TABLE_ID, userID, xla_column_defs, MAX_XLA_COLUMNS, &ncols); if (rc != SQL_SUCCESS { /* See "Handling XLA errors" on page 67 */ }図3.9に示すように、ttXlaGetColumnInfo関数は次の情報を出力します。
図3.9 ttXlaGetColumnInfo()によって返されるttXlaColDesc_t構造体
図3.9に示すように、ttXlaGetColumnInfoによって返される各ttXlaColDesc_t構造体には、その列のオフセット位置を記述するoffset値が含まれます。列データを読み込むためにこのoffset値の使用方法は、列が固定長データ(CHAR、NCHAR、INTEGER、BINARY、DOUBLE、FLOAT、DATE、TIME、TIMESTAMPなど)を含んでいるか、可変長データ(VARCHAR、NVARCHAR、VARBINARYなど)を含んでいるかによって異なります。
固定長列データの場合、列のアドレスは、ttXlaColDesc_t構造体のoffset値に行のアドレスを追加したものです。
図3.10
行内の固定長データの検出
SCOTT.MYDATA表の最初の列はCHAR型です。HandleChange()関数(例3.4)で前に取得したtup1行のアドレス、およびttXlaGetColumnInfo関数(例3.5を参照)によって返された最初のttXlaColDesc_t構造体のoffsetを使用する場合は、次のように入力して最初の列の値を取得します。
SCOTT.MYDATA表の3番目の列はINTEGER型です。3番目のttXlaColDesc_t構造体のoffsetを使用し、値を検出して整数として再キャストします(データが適切に配置されます)。
SCOTT.MYDATA表の4番目の列はNCHAR型です。4番目のttXlaColDesc_t構造体のoffsetを使用し、値を検出してSQLWCHAR型として再キャストできます。
SQLWCHAR * Column4; Column4 = (SQLWCHAR*) ((unsigned char*) tup + xla_column_defs[3].offset);前述の例で取得した列値とは異なり、Column4は2バイトのUnicode文字の配列を指します。文字列を取得するには、例3.16のSQL_WCHARの場合のように、この配列内の各要素に対して繰り返し実行する必要があります。
SQL_WCHARデータ型およびUnicodeデータへのアクセス方法の詳細は、「Unicodeの使用方法」を参照してください。
その他の固定長データ型は、対応するCの型にキャストできます。複合固定長データ型(DATE、TIME、DECIMAL値など)は、TimesTenの内部形式で格納されますが、アプリケーションでXLA変換関数を使用して、対応するODBC C値に変換できます(「複合データ型の変換」を参照)。
NOT INLINE可変長データ(VARCHAR、NVARCHARおよびVARBINARY)の場合、ttXlaColDesc_t→offsetにあるデータは、返された行の可変長部分でのデータの場所を指す4バイトのオフセット値です。オフセット値にオフセット・アドレスを追加して、行の可変長部分の列データのアドレスを取得できます。この場所の最初のnバイト(nは、32ビット・プラットフォームでは4、64ビット・プラットフォームでは8)はデータの長さです。この後に実際のデータが続きます。可変長データの場合、ttXlaColDesc_t→sizeの値は、列の許容最大サイズです。
図3.11 行内のNOT INLINE可変長データの検出
SCOTT.MYDATA表の例で、返された行(tup1)の2番目の列はVARCHAR型です。行内の可変長データを検出するには、まず図3.11に示すように、行の固定長部分で列のttXlaColDesc_t→offsetの値を検出します。このアドレスの値は、行の可変長部分のデータの4バイトのオフセットです(VarOffset)。次に、VarOffsetのアドレスにVarOffsetオフセット値を追加して、可変長列データの先頭へのポインタを取得します(DataLength)。32ビット・プラットフォームで処理が行われると想定すると、DataLength位置の最初の4バイトがデータの長さです。DataLengthの後の次のバイトが、実際のデータの先頭です(Column2)。
void * VarOffset; /* offset of data */ long * DataLength; /* length of data */ char * Column2; /* pointer to data */ VarOffset = (void*) ((unsigned char*) tup1 + xla_column_defs[1].offset); /* * If column is out-of-line, pColVal points to an offset * else column is inline so pColVal points directly to the string length. */ if (xla_column_defs[1].flags & TT_COLOUTOFLINE) DataLength = (long*)((char*)VarOffset + *((int*)VarOffset)); else DataLength = (long*)VarOffset; Column2 = (char*)(DataLength+1);VARBINARY型は、VARCHAR型での方法とほぼ同じ方法で処理されます。Column2は、NVARCHAR型の場合、SQLWCHARとして初期化し、前述のVARCHARの場合と同様に値を取得し、次に例3.16のNCHAR値CharBufの場合と同様にColumn2配置に対して繰り返し実行できます。
レコードの行データから返される文字列はヌル文字では終了されません。文字列をバッファにコピーし、その文字列の最後の文字の後にヌル文字を追加すると、文字列をヌル文字で終了できます。
文字列をヌル文字で終了する手順は、文字列が固定長の場合と可変長の場合で少し異なります。例3.10に、固定長文字列をヌル文字で終了する手順を示します。例3.11にはサイズがわかっている可変長文字列をヌル文字で終了する場合の手順、例3.12にはサイズがわかっていない可変長文字列を終了する場合の手順を示します。
例3.6で返された固定長CHAR(10) Column1文字列をヌル文字で終了するには、文字列+ヌル文字を保持できる十分な大きさのバッファを設定します。次に、ttXlaColDesc_t→sizeから文字列のサイズを取得し、文字列をバッファにコピーして、文字列の末尾をヌル文字で終了します。これで、バッファの内容を使用できます。ここでは、文字列を出力します。
char buffer[10+1]; int size; size = xla_column_defs[0].size; memcpy(buffer, Column1, size); buffer[size] = (char)NULL; printf(" Row %s is %s\n", ((unsigned char*) xla_column_defs[0].colName), buffer);可変長文字列をヌル文字で終了する手順は、固定長文字列の手順とほぼ同じです。可変長データのオフセットの先頭にある値は、文字列のサイズのみです(NOT INLINE可変長列データの読取りを参照)。
例3.9で取得したColumn2文字列がVARCHAR(32)の場合、文字列+ヌル文字を保持できる十分な大きさのバッファを設定します。DataLengthオフセットにある値を使用して文字列のサイズを設定します。
char buffer[32+1]; memcpy(buffer, Column2, *DataLength); buffer[*DataLength] = (char)NULL; printf(" Row %s is %s\n", ((unsigned char*) xla_column_defs[1].colName), buffer);すべてのデータ型を読み取るための汎用コードを記述する場合は、返される文字列のサイズを想定できません。サイズがわからない文字列に対しては、返された文字列の大部分を保持できる十分な大きさのバッファを静的に割り当てます。返された文字列がバッファより大きい場合は、例3.12に示すように、正しいサイズのバッファを動的に割り当てます。
例3.9で取得したColumn2文字列のサイズがわからない場合は、最大10000文字の文字列を保持できる十分な大きさのバッファを静的に割り当てます。その後、可変長データ・オフセットの先頭で取得したDataLength値が、バッファのサイズより小さいことを確認します。文字列がバッファより大きい場合は、mallocを使用してバッファを正しいサイズに動的に割り当てます。
#define STACKBUFSIZE 10000 char VarStackBuf[STACKBUFSIZE]; char * buffer; buffer = (*DataLength+1 <= STACKBUFSIZE) ? VarStackBuf : malloc(*DataLength+1); memcpy(buffer,Column2,*DataLength); buffer[*DataLength] = (char)NULL; printf(" Row %s is %s\n", ((unsigned char*) xla_column_defs[1].colName), buffer); if (buffer != VarStackBuf) /* buffer was allocated */ free(buffer);
DATE、TIME、DECIMAL値などの複合データ型は、TimesTenの内部形式で保存されます。これらはXLA変換関数を使用して、対応するODBC C値に変換できます。
これらの変換関数は、ttXlaUpdateDesc_t型(UPDATETUP、INSERTTUPおよびDELETETUP)に含まれる行データで使用できます。
HandleChange()関数(例3.4を参照)で取得したtup1行のアドレス、およびttXlaGetColumnInfo関数(例3.5を参照)によって返された5番目のttXlaColDesc_t構造体のoffsetを使用すると、TIMESTAMP型の列値を検出できます。ttXlaTimeStampToODBCCType関数を使用し、TimesTen形式から列データを変換し、変換されたTIME値をODBC TIMESTAMP_STRUCTに保存します。この例では、値を出力します。
void * Column5; TIMESTAMP_STRUCT timestamp; Column5 = (void*) ((unsigned char*) tup1 + xla_column_defs[4].offset); rc = ttXlaTimeStampToODBCCType(Column5, ×tamp); if (rc != SQL_SUCCESS) { /* See "Handling XLA errors" on page 67 */ } printf(" %s: %02d-%02d-%02d %02d:%02d:%02d.%06d\n", ((unsigned char*) xla_column_defs[i].colName), timestamp.year,timestamp.month, timestamp.day, timestamp.hour,timestamp.minute,timestamp.second, timestamp.fraction);
HandleChange()関数(例3.4を参照)で取得したtup1行のアドレス、およびttXlaGetColumnInfo関数(例3.5を参照)によって返された6番目のttXlaColDesc_t構造体のoffsetを使用すると、DECIMAL型の列値を検出できます。ttXlaDecimalToCString関数を使用して、TimesTenのDECIMAL形式から文字列に列データを変換します。この例では、値を出力します。
char decimalData[50]; Column6 = (float*) ((unsigned char*) tup + xla_column_defs[5].offset); precision = (short) (xla_column_defs[5].precision); scale = (short) (xla_column_defs[5].scale); rc = ttXlaDecimalToCString(Column6, (char*)&decimalData, precision, scale); if (rc != SQL_SUCCESS) { /* See "Handling XLA errors" on page 67 */ } printf(" %s: %s\n", ((unsigned char*) xla_column_defs[5].colName), decimalData);
NULL値を指定可能な列では、ttXlaColDesc_t→nullOffsetはレコードのNULLバイトを指します。nullOffsetは、列がNULLである場合は1、NULLでない場合は0(ゼロ)となります。
列値がNULLであるかどうかを判断するには、まずnullOffsetが0(ゼロ)かどうかを確認します。0の場合は、NULL値可能ではありません。nullOffsetがNULL値可能である場合は、nullOffsetの値が1か0かを確認します。
Column6がNULLであるかどうかを確認するには、次のように入力します。
if (xla_column_defs[5].nullOffset != 0) { if (*((unsigned char*) tup + xla_column_defs[5].nullOffset) == 1) { printf("Column6 is NULL\n"); } }
例3.16に示すように、前述のすべての列確認コードをPrintColValues()関数にまとめてみます。この例では、各列のttXlaColDesc_t→dataTypeを確認し、データ型がCHAR、NCHAR、INTEGER、TIMESTAMP、DECIMAL、およびVARCHARである列を検出して、その値を出力します。これは、この例でのみ使用する方法です。読み取る列を識別する方法およびその結果への対応は、ユーザーが決定できます。たとえば、別の方法として、ttXlaColDesc_t→ColNameの値を確認して、名前で特定の列を検出する方法もあります。
まず、ttXlaColDesc_t→NullOffsetを調べて列がNULLかどうかを確認します。次に、ttXlaColDesc_t→dataTypeのフィールドを調べて列のデータ型を確認します。単純な固定長データ(CHAR、NCHARおよびINTEGER)の場合は、ttXlaColDesc_t→offsetにある値を適切なCの型にキャストします。複合データ型TIMESTAMPおよびDECIMALは、XLA変換関数ttXlaTimeStampToODBCCTypeおよびttXlaDecimalToCStringを使用して、TimesTen形式からODBC C値に変換されます。
可変長データ(VARCHAR)の場合は、行の可変長部分でデータを検出します(「NOT INLINE可変長列データの読取り」を参照)。
void PrintColValues(void * tup) { SQLRETURN rc; SQLINTEGER native_error; void * pColVal; char buffer[50+1]; /* No strings over 50 bytes */ int i, j, size; for (i = 0; i < ncols; i++) /* ncols from ttXlaGetColumnInfo */ { if (xla_column_defs[i].nullOffset != 0) { /* Is col NULL? */ if (*((unsigned char*) tup + xla_column_defs[i].nullOffset) == 1) { /* If so... */ printf(" %s: NULL\n", ((unsigned char*) xla_column_defs[i].colName)); continue; /* Skip rest and re-loop */ } }固定長データ型を処理する場合:
/* For INTEGER, recast as int */ if (xla_column_defs[i].dataType == SQL_INTEGER) { printf(" %s: %d\n", ((unsigned char*) xla_column_defs[i].colName), *((int*) ((unsigned char*) tup + xla_column_defs[i].offset))); } /* For CHAR, just get value and null-terminate string */ else if (xla_column_defs[i].dataType == SQL_CHAR) { pColVal = (void*) ((unsigned char*) tup + xla_column_defs[i].offset); memcpy(buffer, pColVal, xla_column_defs[i].size); buffer[xla_column_defs[i].size] = (char)NULL; printf(" %s: %s\n", ((unsigned char*) xla_column_defs[i].colName), buffer); } /* For NCHAR, recast as SQLWCHAR. NCHAR strings must be parsed one character at a time */ else if (xla_column_defs[i].dataType == SQL_WCHAR) { SQLWCHAR * CharBuf; CharBuf = (SQLWCHAR*) ((unsigned char*) tup + xla_column_defs[i].offset); printf(" %s: ", ((unsigned char*) xla_column_defs[i].colName)); for (j = 0; j < xla_column_defs[i].size / 2; j++) { printf("%c", CharBuf[j]); } printf("\n"); }可変長データ型を処理する場合:
/* For VARCHAR, locate value at its varaible-length offset and null-terminate.*/ /* VARBINARY types are handled in a similar manner. */ /* For NVARCHARs, initialize 'var_data' as a SQLWCHAR, get the value as shown below, then iterate through 'var_len' as shown for NCHAR above */ else if (xla_column_defs[i].dataType == SQL_VARCHAR) { long * var_len; char * var_data; pColVal = (void*) ((unsigned char*) tup + xla_column_defs[i].offset); /* * If column is out-of-line, pColVal points to an offset * else column is inline so pColVal points directly to the string length. */ if (xla_column_defs[i].flags & TT_COLOUTOFLINE) var_len = (long*)((char*)pColVal + *((int*)pColVal)); else var_len = (long*)pColVal; var_data = (char*)(var_len+1); memcpy(buffer,var_data,*var_len); buffer[*var_len] = '\0'; printf(" %s: %s\n", ((unsigned char*) xla_column_defs[i].colName), buffer); }XLA変換メソッドを使用して複合データ型を変換する場合:
/* Read and convert a TimesTen TIMESTAMP value. DATE and TIME types are handled in a similar manner */ else if (xla_column_defs[i].dataType == SQL_TIMESTAMP) { TIMESTAMP_STRUCT timestamp; pColVal = (void*) ((unsigned char*) tup + xla_column_defs[i].offset); rc = ttXlaTimeStampToODBCCType(pColVal, ×tamp); if (rc != SQL_SUCCESS) { /* See "Handling XLA errors" on page 67 */ } printf(" %s: %02d-%02d-%02d %02d:%02d:%02d.%06d\n", ((unsigned char*) xla_column_defs[i].colName), timestamp.year,timestamp.month, timestamp.day, timestamp.hour,timestamp.minute, timestamp.second,timestamp.fraction); } /* Read and convert a TimesTen DECIMAL value to a string. */ else if (xla_column_defs[i].dataType == SQL_DECIMAL) { char decimalData[50]; short precision, scale; pColVal = (float*) ((unsigned char*) tup + xla_column_defs[i].offset); precision = (short) (xla_column_defs[i].precision); scale = (short) (xla_column_defs[i].scale); rc = ttXlaDecimalToCString(pColVal, (char*)&decimalData, precision, scale); if (rc != SQL_SUCCESS) { /* See "Handling XLA errors" on page 67 */ } printf(" %s: %s\n", ((unsigned char*) xla_column_defs[i].colName), decimalData); } } /* End FOR loop */ }
ODBCまたはXLA関数をコールするたびに、エラーのリターン・コードを確認する必要があります。エラーが致命的である場合は、「XLAアプリケーションの終了」の説明に従ってプログラムを終了します。
エラーは、エラー番号またはtt_Err文字列のいずれかを使用して確認できます。TimesTenエラー(tt_Err文字列および関連する番号の両方)の完全なリストについては、install_dir/include/tt_errCode.hファイルを参照してください。各メッセージについては、『Oracle TimesTen In-Memory Database APIおよびSQLリファレンス・ガイド』の警告およびエラーに関する章のエラーおよび警告のリストの項を参照してください。
この項では、XLAエラーの処理方法について説明します。ODBCエラーの処理方法については、「エラーのチェック」を参照してください。
XLA関数のリターン・コードがSQL_SUCCESSでない場合は、ttXlaError関数を使用して、XLAハンドルでXLA固有のエラーを取得します。
たとえば、ttXlaTableByNameなどのXLA関数をコールした後、リターン・コードがSQL_SUCCESSであるかどうかを確認できます。SQL_SUCCESSではない場合は、XLAエラー処理関数をコールしてアプリケーションを終了できます。
rc = ttXlaTableByName(xla_handle, TABLE_OWNER, TABLE_NAME, &SYSTEM_TABLE_ID, &userID); if (rc != SQL_SUCCESS) { handleXLAerror (rc, xla_handle, err_buf, &native_error); fprintf(stderr, "ttXlaTableByName() returns an error <%d>: %s", rc, err_buf); TerminateGracefully(1); }XLAエラー処理関数は、エラー・スタックからすべてのXLAエラーが読み取られるまで繰り返しttXlaErrorをコールします(ttXlaErrorからのリターン・コードはSQL_NO_DATA_FOUNDです)。エラーを再度読み取る必要がある場合は、ttXlaErrorRestart関数をコールしてエラー・スタックのポインタを最初のエラーにリセットします。
エラー・スタックは、ttXlaErrorまたはttXlaErrorRestart以外のすべてのXLA関数へのコール後に消去されます。
アプリケーションによっては、特定のXLAエラーに対処する必要がある場合があります。次に、それらのエラーを示します。
tt_ErrDbAllocFailed 802 T tt_ErrCacheXlaNotRead 5023 tt_ErrCacheXLARollbackFailed 5031 tt_ErrCondLockConflict 6001 T tt_ErrDeadlockVictim 6002 T tt_ErrTimeoutVictim 6003 T tt_ErrPermSpaceExhausted 6220 T tt_ErrTempSpaceExhausted 6221 T tt_ErrBadXlaRecord 8024 tt_ErrXlaBookmarkUsed 8029 tt_ErrXlaBookmarkBad 8030 tt_ErrXlaLsnBad 8031 tt_ErrXlaNoSQL 8034 tt_ErrXlaNoLogging 8035 tt_ErrXlaParameter 8036 tt_ErrXlaTableDiff 8037 tt_ErrXlaTableSystem 8038 tt_ErrXlaTupleMismatch 8046 tt_ErrXlaDedicatedConnection 8047
これらのエラーを含むエラーの詳細は、『Oracle TimesTen In-Memory Database APIおよびSQLリファレンス・ガイド』の警告およびエラーに関する章のエラーおよび警告のリストの項を参照してください。
xlaSimple.cデモでは、ttXlaTableByNameなどのXLA関数をコールした後、リターン・コードがSQL_SUCCESSでない場合は、handleXLAerrorエラー処理関数をコールしてエラーを取得し、TerminateGracefully関数をコールしてアプリケーションを終了します(「XLAアプリケーションの終了」を参照)。
rc = ttXlaTableByName(xla_handle, TABLE_OWNER, TABLE_NAME, &SYSTEM_TABLE_ID, &userID); if (rc != SQL_SUCCESS) { handleXLAerror (rc, xla_handle, err_buf, &native_error); fprintf(stderr, "ttXlaTableByName() returns an error <%d>: %s", rc, err_buf); TerminateGracefully(1); }xlaSimple.cデモのhandleXLAerror関数は、次のようになります。
void handleXLAerror(SQLRETURN rc, ttXlaHandle_h xlaHandle, SQLCHAR * err_msg, SQLINTEGER * native_error) { #ifdef _WIN32 long retLen; #else int retLen; #endif /* _WIN32 */ SQLCHAR message[1024]; SQLINTEGER code; char * err_msg_ptr; /* initialize return codes */ rc = SQL_ERROR; *native_error = -1; err_msg[0] = '\0'; err_msg_ptr = (char*) &err_msg[0]; while (1) { int rc = ttXlaError(xlaHandle, &code, message, sizeof(message), &retLen); if (rc == SQL_NO_DATA_FOUND) { break; } if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { sprintf(err_msg_ptr, "*** Error fetching error message via ttXlaError(); rc=<%d>.",rc) ; break; } rc = SQL_ERROR; *native_error = code ; /* append any other error messages */ err_msg_ptr = (char*) &err_msg[0] + strlen((char*)err_msg); } */ end while loop */ }
XLAアプリケーションでトランザクション・ログの読取りを終了した後、監視中の表またはマテリアライズド・ビュー(あるいはその両方)をサブスクライブ解除し、コミットされていないトランザクションをロールバックし、すべてのハンドルを解放して、正常に終了する必要があります。プログラム終了時に、XLAブックマークを削除するかどうかは場合によります(「ブックマークの削除」を参照)。
リソースは、割当ての順序とは逆の順序で解放します。XLAによって追跡された表およびマテリアライズド・ビューの場合は、ttXlaTableStatus関数をコールし、newstatusパラメータを0(ゼロ)に設定します。これによって、表およびマテリアライズド・ビューがXLAからサブスクライブ解除されます。次に、ttXlaCloseをコールしてXLAハンドル(xla_handle)を解放します。
SQL_ROLLBACKを指定してSQLTransact関数をコールし、コミットされていないトランザクションをすべてロールバックします。次に、SQLDisconnectをコールしてTimesTenへの接続をクローズします。最後に、SQLFreeConnectおよびSQLFreeEnv関数をコールして、接続(hdbc)ハンドルおよび環境(henv)ハンドルを解放し、関連するすべてのメモリーを解放します。
たとえば、xlaSimple.cデモのTerminateGracefully関数は、次のようになります。
void TerminateGracefully(int status) { SQLRETURN rc; SQLINTEGER native_error ; SQLINTEGER oldstatus; SQLINTEGER newstatus = 0; /* If the table has been subscribed to via XLA, unsubscribe it */ if (SYSTEM_TABLE_ID != 0) { rc = ttXlaTableStatus(xla_handle, SYSTEM_TABLE_ID, 0, &oldstatus, &newstatus); if (rc != SQL_SUCCESS) { /* See "Handling XLA errors" on page 67 */ } SYSTEM_TABLE_ID = 0; } /* Close the XLA connection. */ if (xla_handle != NULL) { rc = ttXlaClose(xla_handle); if (rc != SQL_SUCCESS) { fprintf(stderr, "Error when disconnecting from XLA:<%d>", rc); } xla_handle = NULL; } /* Disconnect from TimesTen entirely. */ if (hdbc != SQL_NULL_HDBC) { rc = SQLTransact(henv, hdbc, SQL_ROLLBACK); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { /* See "Handling XLA errors" on page 67 */ } rc = SQLDisconnect(hdbc); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { /* See "Handling XLA errors" on page 67 */ } rc = SQLFreeConnect(hdbc); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { /* See "Checking for errors" on page 23 */ } hdbc = SQL_NULL_HDBC; } if (henv != SQL_NULL_HENV) { rc = SQLFreeEnv(henv); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { /* See "Checking for errors" on page 23 */ } henv = SQL_NULL_HENV; } exit(status); }
XLAアプリケーションを終了する前に、ttXlaDeleteBookmark関数を使用して、XLAブックマークを削除できます。ブックマークは、前の接続がクローズされた後に新しい接続で再利用できます(「XLAブックマークについて」を参照)。この場合、新しい接続は、前の接続が停止した場所からトランザクション・ログの読取りを再開します。次に、ブックマークを削除した場合のメリットおよびデメリットを示します。
xlaSimple.cデモのInitHandler関数は、終了時にXLAブックマークを削除します。
if (deleteBookmark) { ttXlaDeleteBookmark(xla_handle); if (rc != SQL_SUCCESS) { /* See "Handling XLA errors" on page 67 */ } xla_handle = NULL; /* Deleting the bookmark has the */ /* effect of disconnecting from XLA. */ } /* Close the XLA connection, as described in "Terminating an XLA application" on page 70. */